Aproaches to achieve the following qualities in a module:
1. It provides business value
2. Magento can be upgraded without breaking the module
3. It is easy to understand and modify
4. It has good test coverage
5. It is free of bugs
3. Introduction
Magento 1.x
It has contributed to the collective knowledge
of the PHP community.
Both as a good and a bad example :)
3 The beautiful Magento Module November, 1st 2014
4. Introduction
This is not
an introduction into
Magento development.
4 The beautiful Magento Module November, 1st 2014
5. Introduction
How can
a Magento module
be "beautiful"?
5 The beautiful Magento Module November, 1st 2014
6. Introduction
Qualities of a beautiful module
1.It provides business value
2.Magento can be upgraded without breaking the module
3.It is easy to understand and modify
4.It has good test coverage
5.It is free of bugs
6 The beautiful Magento Module November, 1st 2014
8. Principles and Patterns
Robert C. Martin (Uncle Bob)
8 The beautiful Magento Module November, 1st 2014
9. Principles and Patterns
Some principles and some of their fancy acronyms
•Single Responsibility Principle (SRP)
•Open Closed Principle (OCP)
•Liskov Substitution Principle (LSP)
•Interface Segregation Principle (ISP)
•Dependency Inversion Principle (DIP)
9 The beautiful Magento Module November, 1st 2014
10. Principles and Patterns
Some more principles and some more fancy acronyms
•Encapsulate what varies (ECV)
•Strive for loosely coupled designs between objects that interact (LC)
•Favor composition over inheritance (FCoI)
•Only talk to friends (LoD)
•Don't call us, we'll call you (IoC)
10 The beautiful Magento Module November, 1st 2014
11. Principles and Patterns
What have you done for
me lately, principles?
11 The beautiful Magento Module November, 1st 2014
12. Principles and Patterns
Like with all good
things in life,
there can be too much.
12 The beautiful Magento Module November, 1st 2014
14. “Classes should be open for extension but
closed for modification”
The Open Closed Principle
Uncle Bob
14 The beautiful Magento Module November, 1st 2014
15. The Open Closed Principle
Does Magento have it?
15 The beautiful Magento Module November, 1st 2014
16. The Open Closed Principle
Configuration XML merging
Often the cause of conflicts
16 The beautiful Magento Module November, 1st 2014
17. The Open Closed Principle
Lets look deeper
17 The beautiful Magento Module November, 1st 2014
18. The Open Closed Principle
Self-Documenting Code
Method visibility and naming
18 The beautiful Magento Module November, 1st 2014
19. The Open Closed Principle
public Methods
Are an open invitation to the world
19 The beautiful Magento Module November, 1st 2014
20. The Open Closed Principle
public Methods
Once published should never change
their signature or functionality
20 The beautiful Magento Module November, 1st 2014
21. The Open Closed Principle
protected Methods
Communicate “override me!”
21 The beautiful Magento Module November, 1st 2014
22. The Open Closed Principle
protected Methods
Change in signature or functionality
risks breaking subclasses
22 The beautiful Magento Module November, 1st 2014
23. The Open Closed Principle
private Methods
Safe to change
23 The beautiful Magento Module November, 1st 2014
24. The Open Closed Principle
How is method visibility used in
Magento?
Only public and protected
A too open design!
24 The beautiful Magento Module November, 1st 2014
25. The Open Closed Principle
A good example of
method visibility documenting
how to extend a class.
25 The beautiful Magento Module November, 1st 2014
26. A good example from the core how method visibility helps
abstract class Mage_Core_Block_Abstract ...
{
final public function toHtml()
{
// ...housekeeping
$html = $this->_toHtml();
// ...more housekeeping
return $html;
}
protected function _toHtml()
{
return '';
}
26 The beautiful Magento Module November, 1st 2014
27. The Open Closed Principle
Another good example of OCP
Event Observers
27 The beautiful Magento Module November, 1st 2014
28. The Open Closed Principle
How can we apply this principle
to our modules?
By dispatching events
28 The beautiful Magento Module November, 1st 2014
30. The Open Closed Principle
Favor domain events over
instructional events
30 The beautiful Magento Module November, 1st 2014
31. The Open Closed Principle
Choose the right method visibility
Use private as the default
31 The beautiful Magento Module November, 1st 2014
32. The Open Closed Principle
Use the Magento Factory Methods
and Class Aliases
32 The beautiful Magento Module November, 1st 2014
33. Rewrites are applied during class name resolution.
Using a Factory Method with a PHP class name
is no better then using new.
$notLikeThis = new Mage_Catalog_Model_Product();
$badExample = Mage::getModel('Mage_Catalog_Model_Product');
33 The beautiful Magento Module November, 1st 2014
34. Rewrites are applied during class name resolution.
Using class aliases allows for rewrites.
$correctlyInstantiated = Mage::getModel('catalog/product');
gist of class name resolution steps:
http://vin.ai/class-name-resolution
34 The beautiful Magento Module November, 1st 2014
35. To keep our code open for extension,
we should use class aliases for our classes.
$model = Mage::getModel('example_module/import_parser_xml');
$resource = Mage::getResourceModel('example_module/search');
$helper = Mage::helper('example_module');
$block = Mage::app()->getLayout()->createBlock('example_module/list');
35 The beautiful Magento Module November, 1st 2014
37. “Encapsulate the Concept that Varies, i.e. a
design is better when those parts that vary
are encapsulated in a separate module”
On the Criteria To Be Used in
Decomposing Systems into Modules
David Parnas
37 The beautiful Magento Module November, 1st 2014
38. Encapsulate what varies
Keep changes local so they don't
affect other parts of the system
38 The beautiful Magento Module November, 1st 2014
39. Encapsulate what varies
Magento, do you ECV?
39 The beautiful Magento Module November, 1st 2014
40. Encapsulate what varies
First some "missed opportunities"
40 The beautiful Magento Module November, 1st 2014
41. Encapsulate what varies
Configuration of the
Tax Calculation Algorithm
41 The beautiful Magento Module November, 1st 2014
42. Choosing the tax calculation algorithm based on the system
configuration
// from Mage_Tax_Model_Sales_Quote_Tax::collect()
switch ($this->_config->getAlgorithm($this->_store)) {
case Mage_Tax_Model_Calculation::CALC_UNIT_BASE:
$this->_unitBaseCalculation($address, $request);
break;
case Mage_Tax_Model_Calculation::CALC_ROW_BASE:
$this->_rowBaseCalculation($address, $request);
break;
case Mage_Tax_Model_Calculation::CALC_TOTAL_BASE:
$this->_totalBaseCalculation($address, $request);
break;
default:
break;
}
42 The beautiful Magento Module November, 1st 2014
43. Encapsulate what varies
Order State Management
43 The beautiful Magento Module November, 1st 2014
44. Example of sales/order state behavior management
public function canCancel()
{
if (!$this->_canVoidOrder()) {
return false;
}
if ($this->canUnhold()) { // $this->isPaymentReview()
return false;
}
// ... more of the same + comments removed for brevity...
$state = $this->getState();
if ($this->isCanceled() || $state === self::STATE_COMPLETE ||
$state === self::STATE_CLOSED) {
return false;
}
if ($this->getActionFlag(self::ACTION_FLAG_CANCEL) === false) {
return false;
}
return true;
}
44 The beautiful Magento Module November, 1st 2014
45. Theoretical example of encapsulating the stateful behavior
class Mage_Sales_Model_Order_State_Complete implements OrderState
{
public function canInvoice()
{
return false;
}
public function canShip()
{
return false;
}
public function canReorder()
{
return true;
}
// ...
45 The beautiful Magento Module November, 1st 2014
46. Encapsulate what varies
Where does the core adhere to the
principle, at least in parts?
46 The beautiful Magento Module November, 1st 2014
47. Encapsulate what varies
Total Models
Total calculation
Preparation of display data
47 The beautiful Magento Module November, 1st 2014
48. Encapsulate what varies
EAV Attribute Models
Backend,
Source and
Frontend models
48 The beautiful Magento Module November, 1st 2014
49. Encapsulate what varies
How can we apply this principle
in our Modules?
49 The beautiful Magento Module November, 1st 2014
50. Encapsulate what varies
A simple example scenario
Add information
depending on a the attribute set
to the product detail page.
50 The beautiful Magento Module November, 1st 2014
51. Encapsulate what varies
A simple example scenario
Kitchen Attribute Set
Chair Attribute Set
Insurance Attribute Set
51 The beautiful Magento Module November, 1st 2014
52. First try, not adhering to the principle
private function getTemplateForAttributeSet()
{
switch ($this->getAttributeSetId()) {
case $this->kitchenAttributeSetId:
return 'example/attributesetinfo/kitchen-info.phtml';
case $this->chairAttributeSetId:
return 'example/attributesetinfo/chair-info.phtml';
case $this->insuranceAttributeSetId:
return 'example/attributesetinfo/insurance-info.phtml';
}
}
52 The beautiful Magento Module November, 1st 2014
53. The block also needs to provide all the methods
called from the templates
private function getTemplateForAttributeSet()
{
switch ($this->getAttributeSetId()) {
case $this->kitchenAttributeSetId:
return 'example/attributesetinfo/kitchen-info.phtml';
case $this->chairAttributeSetId:
return 'example/attributesetinfo/chair-info.phtml';
case $this->insuranceAttributeSetId:
return 'example/attributesetinfo/insurance-info.phtml';
}
}
public function getChairCoverWashingInstructionsPDFUrl() {...}
public function getInsuranceTermsAndConditions() {...}
public function getKitchenDesignPdfUrl() {...}
public function getServicePartnerDirectoryUrl() {...}
53 The beautiful Magento Module November, 1st 2014
54. Encapsulate what varies
Decomposed classes
Kitchen Attribute Set Info Block
Chair Attribute Set Info Block
Insurance Attribute Set Info Block
Attribute Set Info Block Locator
54 The beautiful Magento Module November, 1st 2014
55. The locator extends core/text_list
class Example_AttributeSetInfo_Block_InfoBlockLocator
extends Mage_Core_Block_Text_List
{
// ...
}
55 The beautiful Magento Module November, 1st 2014
56. Select and instantiate the delegate object
protected function _prepareLayout()
{
$infoBlockAlias = $this->_getInfoBlockClassAlias();
if (null !== $infoBlockAlias) {
$this->insert($this->_createChildBlock($infoBlockAlias));
}
}
private function _getInfoBlockClassAlias()
{
switch ($this->getAttributeSetId()) {
case $this->kitchenAttributeSetId:
return 'example_attributesetinfo/info_kitchen';
case $this->chairAttributeSetId:
return 'example_attributesetinfo/info_chair';
case $this->insuranceAttributeSetId:
return 'example_attributesetinfo/info_insurance';
}
}
56 The beautiful Magento Module November, 1st 2014
57. The attribute set dependent differences are encapsulated
class Example_AttributeSetInfo_Block_Info_Chair
extends Mage_Core_Block_Template
{
protected function _prepareLayout()
{
$this->setTemplate('example/attributesetinfo/chair-info.phtml');
}
public function getChairCoverWashingInstructionsPDFUrl()
{
return 'the url of the chair cover washing instructions pdf';
}
}
57 The beautiful Magento Module November, 1st 2014
58. Encapsulate what varies
What would be affected by change?
What would be affected by adding an attribute set?
What would be affected if the output for one attribute set
needs to change?
58 The beautiful Magento Module November, 1st 2014
62. Readable Code
What makes code readable?
Coding Standard
62 The beautiful Magento Module November, 1st 2014
63. Obviously
private function getInfoBlockClassAliasBadExample () {
if ($this->getAttributeSetId()==$this->kitchenAttributeSetId)
return 'example_attributesetinfo/info_kitchen';
if($this->chairAttributeSetId == $this->getAttributeSetId()) return
'example_attributesetinfo/info_chair';
else
// insurance attribute set
return 'example_attributesetinfo/info_insurance';
}
private function getInfoBlockClassAliasGoodExample()
{
switch ($this->getAttributeSetId()) {
case $this->kitchenAttributeSetId:
return 'example_attributesetinfo/info_kitchen';
case $this->chairAttributeSetId:
return 'example_attributesetinfo/info_chair';
case $this->insuranceAttributeSetId:
return 'example_attributesetinfo/info_insurance';
}
}
63 The beautiful Magento Module November, 1st 2014
64. Readable Code
What else makes code readable?
Descriptive method names
64 The beautiful Magento Module November, 1st 2014
65. Readable Code
How can we choose good method
names?
A method should do only one thing.
The name should state that thing.
65 The beautiful Magento Module November, 1st 2014
66. Readable Code
If naming a method is hard split it
into smaller ones.
66 The beautiful Magento Module November, 1st 2014
67. Readable Code
First some examples of method
names that could be improved
67 The beautiful Magento Module November, 1st 2014
68. One bad example straight out of the Magento ORM
protected function _construct()
{
$this->_init('example_module/foo');
}
68 The beautiful Magento Module November, 1st 2014
69. I find this would be nicer to read
protected function _initializeCollection()
{
$this->_setModelClassAlias('example_module/foo');
}
69 The beautiful Magento Module November, 1st 2014
70. Readable Code
Methods returning a boolean value should start with is or has
public function isNewObject() {...}
public function hasChildren() {...}
70 The beautiful Magento Module November, 1st 2014
71. Readable Code
Setters should start with set
$request->setDispatched(false)
71 The beautiful Magento Module November, 1st 2014
72. Readable Code
Some more good bad examples
72 The beautiful Magento Module November, 1st 2014
73. Similar methods need very descriptive names to be self documenting
// from Mage_Catalog_Model_Category
public function getChildrenCategories() {...}
public function getChildrenCategoriesWithInactive() {...}
public function getChildren() {...}
public function getAllChildren(...) {...}
public function getCategories(...) {...}
73 The beautiful Magento Module November, 1st 2014
74. Dead code should be removed without mercy
// from Mage_Core_Model_Email
public function __construct()
{
// TODO: move to config
$this->setFromName('Magento');
$this->setFromEmail('magento@varien.com');
$this->setType('text');
}
74 The beautiful Magento Module November, 1st 2014
75. WTF
// from Mage_Shipping_Model_Carrier_Abstract
public function getTotalNumOfBoxes($weight)
{
// ...
return $weight;
}
75 The beautiful Magento Module November, 1st 2014
76. Wrap up
76 The beautiful Magento Module November, 1st 2014
77. Wrap up
What qualifies a Magento module as beautiful?
1.It provides business value
2.Magento can be upgraded without breaking the module
3.It is easy to understand and modify
4.It has good test coverage
5.It is free of bugs
77 The beautiful Magento Module November, 1st 2014
79. Further reading
Link to Amazon
79 The beautiful Magento Module November, 1st 2014
80. Further reading
Link to Amazon
80 The beautiful Magento Module November, 1st 2014
81. Further reading
Link to Amazon
81 The beautiful Magento Module November, 1st 2014
82. Further reading
Link to Amazon
82 The beautiful Magento Module November, 1st 2014
83. Thank you
83 The beautiful Magento Module November, 1st 2014
84. Questions | Comments
Tweet me @VinaiKopp
http://vinaikopp.com
84 The beautiful Magento Module November, 1st 2014
Hinweis der Redaktion
My name is Vinai Kopp, thanks for being here.
I think it is important to understand as much as possible about Magento 1, as it is the basis for Magento 2, where the framework has received a massive overhaul, but is still based on the same architectural ideas as Magento 1 is.
In fact, I think Magento 1 contributed a lot of education to the PHP community in its time.
And still it can be used as a great example, for both: good and bad design.
The trouble is figuring out which is which :-)
There are many excellent resources available
blogs, IRC, books and videos and classroom trainings as they are offered, amongst others, by Magento U.
If you are new to the Magento framework, I hope this talk will be able to give a little context that might be useful should you decide to learn more.
If you already have a couple of projects or more under your belt, this talk will hopefully facilitate you in gaining a deeper understanding of Magento 1.
I like the poetic connotations of the term “beautiful”. It inspires me to try to think outside of my box.
If you feel uncomfortable with the word “beautiful” please feel free to think “clean” instead.
A module that shows all these qualities is a thing of beauty.
This presentation is about how I try to achieve this goals, and what I learned along the way.
I stumbled over design patterns and principles of object oriented development while working with Magento.
It took me a while to read up about these things, but I have been intrigued ever since. It's genius!
Many of these first principles of object oriented development where formulated by Robert C. Martin, a.k.a. Uncle Bob.
He has provided me with great inspiration!
There are more principles, and sometimes they are known with different names.
By applying principles patterns are discovered.
* SRP
A class should have only one reason to change.
Encapsulate what varies.
* OCP
Classes should be open for extension but closed for modification.
* ISP
Program to interfaces, not implementations.
Depend upon abstractions. Do not depend upon concrete classes.
We'll discuss how a couple of these principles can be applied in custom Magento modules,
and also have a look at concrete code examples in the Magento core.
Applying these principles supports a developer to create a well designed piece of software.
This is true for full applications, but also for Magento modules, though maybe on a smaller scale.
All principles should only be applied with care in areas where they make sense.
Disclaimer:
if you try to apply them all the time, it will lead to over engineered and too complex code.
The first principle I would like to talk about is
The Open Closed Principle
According to this principle, new functionality should be created by writing new code, not by modifying existing code.
Yes it has :)
Node values are overwritten during config XML merging.
That is the basic idea of many ways Magento is open for extension.
It is Magento's implementation of the Plugin Pattern as recorded by Martin Fowler.
In practice it definitely has its uses,
but in the long run customizations based on configuration value overwriting
(and that includes rewrites)
often lead to conflicts.
I believe an important part of the Open Closed Principle is that a piece of software should tell the outside world how to extended it.
For example, on a class level this is where method visibility comes into play.
Visibility and method naming is how classes can be made self documenting.
Public methods are like an open invitation to the world to use them.
Once we declare a public method and publish the class, that method signature can never again be changed without risking external code calling the method to be broken.
Introducing new side effects to a public method that weren't there before is also quite risky.
So what about the internal visibilities, protected and private?
Each of those has a specific purpose, too.
This protected method signature is now frozen. Again, any signature or functionality change might break subclasses!
Private visibility is for the class itself only.
If it requires a change down the road, no problem.
As long as the public and protected interfaces stay the same, there is no problem.
One reason for large technical debt.
Any change has to be awkwardly engineered around the existing interfaces.
At first glance it seems like a great thing to be able to override any method, but in hindsight it probably was a bad design choice.
Many implementation details should have been hidden within private scope visibility.
Exposing a method as public or protected should only be done with good reason.
This is a great example how method visibility can tell developers how a class should be extended.
By the way, this is an example of the Template Method pattern's hook operations.
My favorite way to customize Magento.
As we all know, following best practices it is possible to create customizations that are less conflict prone, without needing to change any existing code.
It is no coincidence that the Event Observer pattern is so wide spread and well known.
Probably this is one of the patterns most developers start using without ever having studied patterns.
How can we follow the Open Closed Principle within custom modules?
By dispatching custom events
Choose events that describe the business logic of your module.
Think of events in terms of Domain Events,
for example this_will_happen or this_just_happened
rather then Instructions as do_this_now.
Of course you need to make the external interface public. But every internal method should be private by default.
Only if you really want to use the Template Method pattern or need to encapsulate variations in a subclass, then make a method protected.
We'd better expose methods as protected only if we want invite other developers to change the implementation of that method.
On that note, when declaring a method as protected, make sure you do not call any other methods of the abstract class from within the method.
A subclass should never call methods of its superclass (the Hollywood Principle).
Even if it has its downsides, class rewrites have their place within the Magento framework.
Using a factory method with a PHP class name is no better then instantiating a class directly using the new operator.
Memorizing the class name resolution process is one of the most beneficial things to improve our effectiveness as a Magento developer.
By using class aliases we can keep our module open for extension.
By choosing the correct method visibility we can define how we want our code to be extendable.
The next principle is "encapsulate what varies"
This is one of my favorite principles.
It closely related to the Single Responsibility principle (SRP)
This pattern was defined by Davod Parnas and popularized by the GoF Design Patterns book.
It is the basis of the Strategy Pattern.
Applying this principle leads to a design with a relatively large number of small classes.
Like all principles, this one also is to help us deal with the changes that occur within software over time.
Like all principles, this one also is to help us deal with the changes that occur within software over time.
It's impossible to anticipate everything that will change or vary over the lifetime of a project, but often things will need to be different depending on circumstances already becomes apparent during development.
So what are examples of this principle in the Magento core?
Maybe it is easier to understand how it looks like when the principle is not adhered to.
Lets see in what kind of situations this principle should be applied.
A bit of background: in Magento different tax calculation algorithms can be configured.
Instead of delegating to a separate class depending on the tax calculation settings, each algorithm is implemented within one class,
The tax total model
This actually doesn't look that bad.
The problem is however, that any change to one of the algorithms, even a small bugfix, can have an impact on the others, too.
Each calculation could have been encapsulated within one class. That would probably be a more robust design.
A smaller class is also easier to read.
This is an especially important aspect for complex things like tax calculation.
The behavior of the order often depends on the order state.
There are many methods to check which actions are possible for an order depending on its current state.
There are many more similar methods, e.g. canInvoice(), canCreditmemo(), canReorder().
I find the method pretty hard to understand.
Imagine a change to one of these methods that are called.
It might affect the behavior other states because of the interdependencies.
That is nice descriptive code!
Encapsulating behavior into different state dependent classes would make things much easier to understand and maintain.
That is so common that it has its own pattern: the State Pattern (GoF).
Instead of having the complete total calculation within one class, it is encapsulated in a list of delegates.
The extensibility this brings has already been used by the core team when implementing the tax total model, which didn't effect for example the shipping total model at all.
One of my favorite parts of Magento's EAV design is the ability to encapsulate business logic on an attribute level using backend and source models.
A custom index needs to be updated after a value changes? No problem, a backend model to the rescue.
An attribute needs to be populated with an entity ID from a collection? Hey, we can use a source model for that. So good :)
Lets have a look at how we can apply encapsulate what varies in our own code.
How about a simple but concrete example.
Lets assume we want to enrich the product detail page with information depending on a products attribute set.
We want to add a new block as a child of the `product.info.media.after` block.
For the examples sake lets assume we have the attribute sets "Kitchen", "Chair", and "Insurance".
You might imagine that each of those needs to display quite different information.
The method chooses a template depending on the current products attribute set.
In addition to choosing the template, the block would also have to provide all the methods called within the different attribute set dependent templates, although each concrete template only needs a subset of those.
Not so nice.
So, how can we apply the ECV principle to clean things up?
We need to encapsulate what varies. Basically, we want a different block for each attribute set.
This leads us to the following classes.
We want one block per attribute set.
We also need one class to decide which attribute set block to use
a Block Locator.
In order to keep things within the Magento framework, lets make the locator a kind of proxy for the info blocks.
This is easiest accomplished by using a `core/text_list` block, which - as you probably know - automatically renders all its child blocks.
The child block will then be the specific attribute info block instance.
This is how an implementation of the Locator could look like.
How is this better then the previous example?
We still have a switch statement.
The difference is this time this is the ONLY responsibility of this class.
The rendering of the specific attribute set dependent information is encapsulated within the child block.
In this example the specific child block sets its appropriate template and provide all the required methods for one attribute set.
If a new attribute set is added, only the Locator class needs to be changed.
If the rendered information for one of the attribute sets needs to change, none of the other attribute sets are affected.
This makes our code more resilient against change.
All the principles help us developers to create more well designed software.
Unfortunately they are outside of the scope of this presentation because of time constraints.
However, if you are not familiar with them, I do encourage you to invest time studying them, and also design patterns.
What else besides Principles and Patterns makes a module beautiful?
Whenever we need to inspect a module we have to read code and understand what it does.
It is a fact that we all spend more time reading code then writing code.
It doesn't matter if it is the coding standard Magento uses is the best one out there or if matches is your personal preference. Use it.
Probably you know this experience... getting some code to review and first having to spend 15 minutes reformatting every file to match the coding standard.
Its easy - all IDEs and most editors allow you to reformat a complete file with a few key strokes.
If I have to review code that doesn't match the standard, my first impression of that developer is that he probably is a stubborn n00b.
What it comes down to is that indention, placements of brackets, whitespace and casing is all consistent.
There are still so many other things we can decide ourselves how they should be.
If it is hard to choose a name for a method, that probably means it does to much and should be split into several smaller methods.
Looks familiar?
What happens during `_init`?
What is the string `example_module/foo`?
The code doesn't tell us.
How could it be better?
Oh, this isn't a model, its a collection we are looking at. :)
Lets say the method names probably could be more descriptive...
What is the difference between all these methods?
Do they return the same or different types?
The method names don't help us.
TODO since 6+ years... ;)
And this is just mean
This is not an ideal. It is a realistic goal, and quite achievable.
Focus on solving real business problems.
Study the principles and patterns,
Write readable code.
Write tests.
The following are some of the best books on development I know. They have inspired me a lot.
If you don't know them already and you want to become a better developer, study them.
Clean Code: A Handbook of Agile Software Craftsmanship
by Robert C. Martin
http://amzn.com/0132350882
Design Patterns - Elements of Reusable Object-Oriented Software
by Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides
http://amzn.com/0201633612
Head First Design Patterns
by Eric and Elisabeth Freeman with Kerry Sierra and Bert Bates
http://amzn.com/0596007124
Softwarequalität in PHP Projekten, 2. Edition (German)
by Sebastian Bergman and Stefan Priebsch
http://www.amazon.de/dp/3446435395
Jon Woodall & the others from MageTitans for inviting me!
Standing on the sholders of giants:
Thank you to Robert C. Martin, Martin Fowler, Kent Beck, Stefan Priebsch and all the others who have taught me so much about software.
Thank you to the Magento Community - inspiration, learning opportunities and friendship galore!
And thanks to Magento, too, for providing us with this playing field! Without you all of us would not be here.
And finally thank you all for listening!